Drools Tutorial
================================

First Rule Language Element
------------

* We are going to add a new drl file (drl = drools rule language)

-  In the package "rules" in "src/main/resources" create a rule file called "lesson1.drl"
using New > Other > Drools > Rule Resource with package "droolscours". 


* Adding a simple condition to a rule

- in the previously created rule, import the Account class: 
import droolscours.Account

- Modify the first rule with a condition that is just a fact of type Account. If the rule is fired, then we shall show the message "The account exists" in the console:

rule "Your First Rule"

    when
    	Account(  )
        //conditions
    then
        System.out.println("The account exists");

end

- Create a class AccountTest in droolscours pachake to test this:

package droolscours;

import org.kie.api.KieServices;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;

import com.sample.DroolsTest.Message;

public class AccountTest {

	public static void main(String[] args) {
		try {
            // load up the knowledge base
	    KieServices ks = KieServices.Factory.get();
    	    KieContainer kContainer = ks.getKieClasspathContainer();
            KieSession kSession = kContainer.newKieSession("ksession-rules");

            // go !
            Account a = new Account();
            kSession.insert(a);
            kSession.fireAllRules();
        } catch (Throwable t) {
            t.printStackTrace();
        }

	}

} 

- Executing it we obtain in the console: The account exists

- NB: drl files represent the "production memory", while the KieSession the "working memory" where we can add facts to it with the method "insert".


* Using callback to log activity in drools runtime

- Add a listener for logging in the AccountTest class:
kSession.addEventListener(new RuleRuntimeEventListener() {
				
				@Override
				public void objectUpdated(ObjectUpdatedEvent arg0) {
					System.out.println("Object Updated\n"
							+ "new content: "+arg0.getObject().toString());
					
				}
				
				@Override
				public void objectInserted(ObjectInsertedEvent arg0) {
					System.out.println("Object inserted\n"
							+ arg0.getObject().toString());
					
				}
				
				@Override
				public void objectDeleted(ObjectDeletedEvent arg0) {
					System.out.println("Object Retracted\n"
							+ arg0.getOldObject().toString());
					
				}
			});


- Each time a fact is inserted, updated or retracted we shall log the event 
and show it on the console. Here we are using the toString method of 
the java instance given (event.getObject().toString()).
We shall add a toString method to all our pojo class Account. To do so we shall 
let eclipse generate it for us: Source > Generate toString(). 

- Change the AccountTest code as follows:
	Account a = new Account();
        a.setAccountNo(10);
        FactHandle handlea = kSession.insert(a);
        a.setBalance(12.0);
        kSession.update(handlea, a);
        kSession.delete(handlea);
        kSession.fireAllRules();
        System.out.println("Close...");

where to update an object, you first have to memorize the fact handle
and the same applies when you want to retract.

This is the output:
  Object inserted
  Account [accountNo=10, balance=0.0]
  Object Updated
  new content: Account [accountNo=10, balance=12.0]
  Object Retracted
  Account [accountNo=10, balance=12.0]
  Close...
where our rule is not fired because the fact Account is deleted.



* When and how is a rule fired?

- Let us consider this code:
  Account a = new Account();
  kSession.insert(a);
  kSession.fireAllRules();
  kSession.fireAllRules();

The rule is fired only once.

- Let us consider this code:
  Account a = new Account();
  kSession.insert(a);
  kSession.fireAllRules();
  a.setAccountNo(10);
  kSession.fireAllRules();

The rule is fired only once.

- Let us consider this code:
  Account a = new Account();
  a.setAccountNo(10);
  FactHandle handlea = kSession.insert(a);
  kSession.fireAllRules();
  a.setBalance(12.0);
  kSession.update(handlea, a); 
  kSession.fireAllRules();

The rule is fired twice.

Here is what is happening when the FireAllRules method is called 
on a statefull session :
- drools will look at all rules that can apply and put it in its agenda
- drools will execute the rule that is on top of its agenda
- once fired, the rule will be deactivated
- We have to tell drools of a state change in one of facts 
in the when part (lhs) to make him reconsidering the rule
- A state change can be an insert, update or delete (retract).



* Use the rules actions for inserting/updating/deleting facts. 

- In AccountingPeriod add startDate (java.util.Date) and endDate (Date)
and generate getters/setters and toString.

- In CashFlow add 
  mvtDate (java.util.Date) 
  amount (double)
  type (int)
  accountNo (long)
and generate getters/setters and toString.

- New Rule:
package droolscours

//list any import classes here.

import droolscours.Account
import droolscours.CashFlow
import droolscours.AccountingPeriod

//declare any global variables here




rule "Your First Rule"

    when
    	Account(  )
        //conditions
    then
        System.out.println("The account exists");

end

rule "Your Second Rule"

    when
        CashFlow(  )
    then
        System.out.println("The cash flow exists, inserting an account period");
        AccountingPeriod newPeriod = new AccountingPeriod();
        insert (newPeriod);

end

rule "Your Third Rule"
    when
        AccountingPeriod(  )
    then
        System.out.println("Accounting period exists");

end

- New Test File:
package droolscours;

import org.kie.api.KieServices;
import org.kie.api.event.rule.ObjectDeletedEvent;
import org.kie.api.event.rule.ObjectInsertedEvent;
import org.kie.api.event.rule.ObjectUpdatedEvent;
import org.kie.api.event.rule.RuleRuntimeEventListener;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.rule.FactHandle;

public class CashFlowTest {
	public static void main(String[] args) {
		try {
            // load up the knowledge base
	        KieServices ks = KieServices.Factory.get();
    	    KieContainer kContainer = ks.getKieClasspathContainer();
        	KieSession kSession = kContainer.newKieSession("ksession-rules");
        	
        	// add a event listener for logging
        	kSession.addEventListener(new RuleRuntimeEventListener() {
				
				@Override
				public void objectUpdated(ObjectUpdatedEvent arg0) {
					System.out.println("Object Updated\n"
							+ "new content: "+arg0.getObject().toString());
					
				}
				
				@Override
				public void objectInserted(ObjectInsertedEvent arg0) {
					System.out.println("Object inserted\n"
							+ arg0.getObject().toString());
					
				}
				
				@Override
				public void objectDeleted(ObjectDeletedEvent arg0) {
					System.out.println("Object Retracted\n"
							+ arg0.getOldObject().toString());
					
				}
			});
        	

            // go !
            CashFlow a = new CashFlow();
            FactHandle handlea = kSession.insert(a);
            kSession.fireAllRules();
            System.out.println("Close...");
        } catch (Throwable t) {
            t.printStackTrace();
        }

	}
}

OUTPUT:
Object inserted
CashFlow [mvtDate=null, amount=0.0, type=0, accountNo=0]
The cash flow exists, inserting an account period
Object inserted
AccountingPeriod [startDate=null, endDate=null]
Accounting period exists
Close...



